package lpcn.xbee;

import java.util.ArrayList;

import org.apache.log4j.Logger;

import com.rapplogic.xbee.api.ApiId;
import com.rapplogic.xbee.api.AtCommand;
import com.rapplogic.xbee.api.AtCommandResponse;
import com.rapplogic.xbee.api.PacketListener;
import com.rapplogic.xbee.api.XBee;
import com.rapplogic.xbee.api.XBeeAddress16;
import com.rapplogic.xbee.api.XBeeAddress64;
import com.rapplogic.xbee.api.XBeeException;
import com.rapplogic.xbee.api.XBeeResponse;
import com.rapplogic.xbee.api.XBeeTimeoutException;
import com.rapplogic.xbee.api.zigbee.AssociationStatus;
import com.rapplogic.xbee.api.zigbee.NodeDiscover;
import com.rapplogic.xbee.api.zigbee.ZNetRxResponse;
import com.rapplogic.xbee.api.zigbee.ZNetTxRequest;
import com.rapplogic.xbee.api.zigbee.ZNetTxStatusResponse;

public class XBeeInterface
{
	
	/* CLASS VARIABLES */
	public XBee xbee;
	public XBeeAddress16 destinationAddress16;
	public XBeeAddress64 destinationAddress64;
	public boolean readyToSend;
	public ZNetTxRequest request;
	public ZNetTxStatusResponse response;
	public ArrayList<XBeeInfo> networkTable;
	
	public final static Logger log = Logger.getLogger(XBeeInterface.class);
	
	/* CLASS METHODS */
	public XBeeInterface(String XBeePort, int XBeeBaud) throws XBeeException
	{
		networkTable = new ArrayList<XBeeInfo>();
		
		xbee = new XBee();
		//try
		//{
			xbee.open(XBeePort, XBeeBaud);
			// wait 1/2 second to allow association with network
			//Thread.sleep(500);
			//xbee.sendAtCommand(new AtCommand("RE"));
			//nodeDiscovery();
		//}
		/*catch (InterruptedException e)
		{
			e.printStackTrace();
		}
		finally
		{
		}*/
		setDestinationAddress64(new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x30, 0xc1, 0x47));
		request = new ZNetTxRequest(new XBeeAddress64(0, 0x13, 0xa2, 0, 0x40, 0x30, 0xc1, 0x47), new int[] { 0x00 });
		request.setFrameId(xbee.getNextFrameId());
		setReadyToSend(true);
	}
	
	public int[] XBeeReceivePacket()
	{
		ZNetRxResponse response = new ZNetRxResponse();
		try
		{
			response = (ZNetRxResponse) xbee.getResponse();
			
		}
		catch (XBeeException e)
		{
			e.printStackTrace();
		}
		return response.getData();
	}
	
	public ZNetTxStatusResponse.DeliveryStatus XBeeSendPacket(int[] payload)
	{
		// Set the destination address and the payload of the packet
		request.setDestAddr64(getDestinationAddress64());
		request.setPayload(payload);
		
		try
		{
			// Send the packet
			response = (ZNetTxStatusResponse) xbee.sendSynchronous(request, 10*1000);
			/*
			System.out.println("---");
			System.out.println("request is: " + request);
			System.out.println("---");
			System.out.println("response is :" + response);
			*/
			
			// Update frame ID for next request
			request.setFrameId(xbee.getNextFrameId());
			
			// If the packet was successfully delivered
			if (response.getDeliveryStatus() == ZNetTxStatusResponse.DeliveryStatus.SUCCESS)
			{
				setReadyToSend(true);
				
				// Check to see if the 16-bit address has changed from the stored value. If it has changed, then update it. This allows for faster routing
				if (response.getRemoteAddress16().equals(XBeeAddress16.ZNET_BROADCAST))
				{
					request.setDestAddr16(response.getRemoteAddress16());
				}
			}
			else
			{
				return ZNetTxStatusResponse.DeliveryStatus.NETWORK_ACK_FAILURE;
			}
		}
		catch (XBeeTimeoutException e)
		{
			//response.setDeliveryStatus(ZNetTxStatusResponse.DeliveryStatus.NETWORK_ACK_FAILURE);
			
			//ZNetTxStatusResponse.DeliveryStatus failed =  
				//NETWORK_ACK_FAILURE;
			//System.out.println("Request timed out");
			System.out.println("Failure! Packet failed to send!");
			System.out.println("The request is:");
			System.out.println(request);
			System.out.println("");
			System.out.println("The response is:");
			System.out.println(response);
			//return response;
			return ZNetTxStatusResponse.DeliveryStatus.NETWORK_ACK_FAILURE;
		}
		catch (XBeeException e)
		{
			return ZNetTxStatusResponse.DeliveryStatus.NETWORK_ACK_FAILURE;
		}
		return response.getDeliveryStatus();
	}
	
	public void XBeeSendAsynchronousPacket(int[] payload)
	{
		// Set the destination address and the payload of the packet
		request.setDestAddr64(getDestinationAddress64());
		request.setPayload(payload);
		
		try
		{
			// Send the packet
			xbee.sendAsynchronous(request);
			
			// Update frame ID for next request
			request.setFrameId(xbee.getNextFrameId());
		}
		catch (XBeeException e)
		{
		}
	}
	
	public void nodeDiscovery() throws XBeeException, InterruptedException {
		try
		{
			// Clear the network table
			networkTable.clear();
			
			// the default Node discovery timeout is 6 seconds
			long nodeDiscoveryTimeout = 6000;
			
			// Add a packet listener to listen for a response
			
			PacketListener packetListenerND = new PacketListener()
			{
				public void processResponse(XBeeResponse response)
				{
					if (response.getApiId() == ApiId.AT_RESPONSE)
					{
						NodeDiscover nd = NodeDiscover.parse((AtCommandResponse)response);
						System.out.println("Node discover response is: " + nd);
						XBeeInfo responder = new XBeeInfo(nd.getNodeIdentifier(), nd.getNodeAddress16(), nd.getNodeAddress64());
						
						// If the responding node is not in the list, add it
						if(!networkTable.contains(responder))
						{
							networkTable.add(responder);
						}
					}
				}
			};
			xbee.addPacketListener(packetListenerND);
			System.out.println("Sending node discover command");
			
			// Send the Node Discovery command
			xbee.sendAsynchronous(new AtCommand("ND"));
			// wait for nodeDiscoveryTimeout milliseconds
			Thread.sleep(nodeDiscoveryTimeout);
			xbee.removePacketListener(packetListenerND);
			System.out.println("Time is up!  You should have heard back from all nodes by now.  If not make sure all nodes are associated and/or try increasing the node timeout (NT)");
		}
		finally
		{
		}
	}
	
	public void associationStatus() throws XBeeException {
		// get association status - success indicates it is associated to another XBee
		AtCommandResponse response = (AtCommandResponse) xbee.sendAtCommand(new AtCommand("AI"));
		System.out.println("Association Status is " + AssociationStatus.get(response));		
	}
	
	/* GETTERS AND SETTERS */
	public void setDestinationAddress16(XBeeAddress16 destinationAddress16) {
		this.destinationAddress16 = destinationAddress16;
	}

	public XBeeAddress16 getDestinationAddress16() {
		return destinationAddress16;
	}
	
	public void setDestinationAddress64(XBeeAddress64 destinationAddress64) {
		this.destinationAddress64 = destinationAddress64;
	}

	public XBeeAddress64 getDestinationAddress64() {
		return destinationAddress64;
	}

	public void setReadyToSend(boolean readyToSend) {
		this.readyToSend = readyToSend;
	}

	public boolean isReadyToSend() {
		return readyToSend;
	}

	public void setRequest(ZNetTxRequest request) {
		this.request = request;
	}

	public ZNetTxRequest getRequest() {
		return request;
	}
}